package com.lifang.framework.aop.framework.support;

import com.lifang.framework.aop.Pointcut;
import com.lifang.framework.aop.aspectj.AbstractAspectJAdvice;
import com.lifang.framework.beans.BeansException;
import com.lifang.framework.beans.factory.BeanFactory;
import com.lifang.framework.beans.factory.BeanFactoryAware;
import com.lifang.framework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.lifang.framework.beans.factory.support.BeanDefinition;
import com.lifang.framework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * aop实现基类
 * 基于bean生命周期实现
 */
public abstract class AbstractAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private final ReflectiveAspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory();

    protected final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(32);

    private final Map<String, List<Advice>> advisorsCache = new ConcurrentHashMap<>();

    protected List<String> aspectBeanNames;

    private DefaultListableBeanFactory beanFactory;

    protected abstract Object wrapIfNecessary(Object bean, String beanName, List<Advice> adviceList);

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (this.advisedBeans.containsKey(beanName)) {
            return null;
        }
        if (isInfrastructureClass(beanClass)) {
            this.advisedBeans.put(beanName, Boolean.FALSE);
        }
        if (aspectBeanNames == null) {
            buildAspectJAdviseList();
        }
        return null;
    }

    /**
     * 初始化后如果有需要，包装bean成代理bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (isInfrastructureClass(bean.getClass()) || this.aspectBeanNames.contains(beanName)) {
            return bean;
        }
        if (this.advisedBeans.containsKey(beanName) && !this.advisedBeans.get(beanName)) {
            return bean;
        }
        return wrapIfNecessary(bean, beanName, findCandidateAdviceList());
    }

    /**
     * bean开始被实例化后，判断每个bean是否需要被代理
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        boolean advisedFlag = false;
        Class<?> beanType = getBeanFactory().getType(beanName);
        for (Advice advice : findCandidateAdviceList()) {
            if (advice instanceof AbstractAspectJAdvice) {
                AbstractAspectJAdvice aspectJAdvice = (AbstractAspectJAdvice) advice;
                if (aspectJAdvice.getPointcut().getClassFilter().matches(beanType)) {
                    advisedFlag = true;
                    break;
                }
            }
        }
        if (!this.aspectBeanNames.contains(beanName)) {
            this.advisedBeans.put(beanName, advisedFlag);
        }
        return true;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, BeanDefinition definition, String beanName) {
        if (isInfrastructureClass(bean.getClass()) || this.aspectBeanNames.contains(beanName)) {
            return bean;
        }
        if (this.advisedBeans.containsKey(beanName) && !this.advisedBeans.get(beanName)) {
            return bean;
        }
        return wrapIfNecessary(bean, beanName, findCandidateAdviceList());
    }

    public DefaultListableBeanFactory getBeanFactory() {
        return beanFactory;
    }

    protected List<Advice> findCandidateAdviceList() {
        if (this.aspectBeanNames.isEmpty()) {
            return Collections.emptyList();
        }

        List<Advice> adviceList = new ArrayList<>();
        for (String aspectName : this.aspectBeanNames) {
            List<Advice> cachedAdvisors = this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                adviceList.addAll(cachedAdvisors);
            }
        }
        return adviceList;
    }

    /**
     * 初始化 Advice
     */
    protected void buildAspectJAdviseList() {
        List<String> aspectNames = this.aspectBeanNames;
        if (aspectNames == null) {
            aspectNames = new ArrayList<>();
            String[] beanNames = this.beanFactory.getBeanDefinitionNames();
            for (String beanName : beanNames) {
                Class<?> beanType = this.beanFactory.getType(beanName);
                if (beanType == null) {
                    continue;
                }
                if (this.aspectJAdvisorFactory.isAspect(beanType)) {
                    aspectNames.add(beanName);
                    List<Advice> classAdviceList = this.aspectJAdvisorFactory.getAdvice(beanType, beanName);
                    this.advisorsCache.put(beanName, classAdviceList);
                }
            }
            this.aspectBeanNames = aspectNames;
        }
    }

    /**
     * 排除使用了Pointcut、Advice注解的基类
     */
    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass);
    }


}
